package procilege.impl.expand;

import org.apache.solr.util.OpenBitSet;

import procilege.compute.ExprExpandTraveller;
import procilege.compute.PlaceType;
import procilege.compute.PlaceTypeMetadata;
import procilege.lang.BoolExprElement;
import procilege.lang.BoolExprOr;
import procilege.lang.EntityExprElement;
import procilege.lang.EntityExprOr;
import procilege.lang.PrivExprComposition;
import procilege.lang.PrivExprElement;
import procilege.lang.PrivExprMergence;

@PlaceTypeMetadata(type = PrivExprMergence.class)
public class PrivExprMergenceExpandPlace implements
		PlaceType<ExprExpandTraveller> {
	
	public void visit(ExprExpandTraveller tr, Object place) {

		PrivExprMergence pv = (PrivExprMergence) place;

		PrivExprComposition plus = new PrivExprComposition();
		OpenBitSet boundnames = pv.getBoundNames();

		// construct the first identity term
		plus.add(new PrivExprMergence(tr.getBoundNames()));

		Object r;

		// expand the normal form of the entity expr
		tr.setout(pv.getEntityExpr());
		r = tr.getPreviousVisitResult();
		if (r != null) {
			PrivExprComposition terms = plus;
			plus = new PrivExprComposition();

			if (r instanceof EntityExprOr) {
				for (EntityExprElement ens : (EntityExprOr) r) {
					for (PrivExprElement pnt : terms) {

						PrivExprMergence prod = new PrivExprMergence(boundnames);
						prod.mergeEntity(ens);
						prod.mergePrivilege(pnt);
						plus.add(prod);
					}
				}
			} else {
				for (PrivExprElement pnt : terms) {

					PrivExprMergence prod = new PrivExprMergence(boundnames);
					prod.mergeEntity((EntityExprElement) r);
					prod.mergePrivilege(pnt);
					plus.add(prod);
				}
			}
		}

		// expand the normal form of predicate expr
		tr.setout(pv.getRestrictionExpr());
		r = tr.getPreviousVisitResult();
		if (r != null) {
			PrivExprComposition terms = plus;
			plus = new PrivExprComposition();

			if (r instanceof BoolExprOr) {
				for (BoolExprElement pes : (BoolExprOr) r) {
					for (PrivExprElement pnt : terms) {

						PrivExprMergence prod = new PrivExprMergence(boundnames);
						prod.mergePrivilege(pnt);
						prod.mergeRestriction(pes);
						plus.add(prod);
					}
				}
			} else {
				for (PrivExprElement pnt : terms) {

					PrivExprMergence prod = new PrivExprMergence(boundnames);
					prod.mergePrivilege(pnt);
					prod.mergeRestriction((BoolExprElement) r);
					plus.add(prod);
				}
			}
		}
		
		for (PrivExprElement t : pv.getPrivileges()) {
			// expand the norm form of each privilege
			PrivExprComposition terms = plus;
			plus = new PrivExprComposition();

			tr.setout(t);
			r = tr.getPreviousVisitResult();
			if (r instanceof PrivExprComposition) {
				for (PrivExprElement p1 : (PrivExprComposition) r) {
					for (PrivExprElement p2 : terms) {
						PrivExprMergence prod = new PrivExprMergence(boundnames);
						prod.mergePrivilege(p1);
						prod.mergePrivilege(p2);
						plus.add(prod);
					}
				}
			} else {
				for (PrivExprElement p2 : terms) {
					PrivExprMergence prod = new PrivExprMergence(boundnames);
					prod.mergePrivilege((PrivExprElement) r);
					prod.mergePrivilege(p2);
					plus.add(prod);
				}
			}
		}
		
		tr.result(plus);
	}
}